package com.znjc.web.controller.device;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.znjc.common.annotation.Log;
import com.znjc.common.constant.DeviceConstants;
import com.znjc.common.core.controller.BaseController;
import com.znjc.common.core.domain.AjaxResult;
import com.znjc.common.core.domain.entity.SysDept;
import com.znjc.common.core.page.TableDataInfo;
import com.znjc.common.enums.BusinessType;
import com.znjc.common.utils.DictUtils;
import com.znjc.common.utils.StringUtils;
import com.znjc.common.utils.poi.ExcelUtil;
import com.znjc.device.domain.DeviceDevice;
import com.znjc.device.domain.DeviceType;
import com.znjc.device.domain.vo.IotData;
import com.znjc.device.service.*;
import com.znjc.device.util.DataUtil;
import com.znjc.system.service.ISysDeptService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.stream.Collectors;

import static com.znjc.common.utils.Threads.sleep;

/**
 * 设备管理Controller
 *
 * @author zengpc
 * @date 2023-12-04
 */
@RestController
@RequestMapping("/device/device")
@Slf4j
public class DeviceDeviceController extends BaseController {
    @Autowired
    private IDeviceDeviceService deviceDeviceService;
    @Autowired
    private IDeviceTypeService deviceTypeService;
    @Autowired
    private ISysDeptService deptService;
    @Autowired
    private IIotSerialService iotSerialService;
    @Autowired
    private DeviceDataService deviceDataService;
    @Autowired
    private IDeviceBoardRefService deviceBoardRefService;
    @Autowired
    private IDeviceRuleRefService deviceRuleRefService;
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * 查询设备管理列表
     */
    @PreAuthorize("@ss.hasPermi('device:device:list')")
    @GetMapping("/list")
    public TableDataInfo list(DeviceDevice deviceDevice) {
        startPage();
        LambdaQueryWrapper<DeviceDevice> wrapper = getWrapper(deviceDevice);
        List<DeviceDevice> list = deviceDeviceService.list(wrapper);
        return getDataTable(packList(list));
    }

    /**
     * 导出设备管理列表
     */
    @PreAuthorize("@ss.hasPermi('device:device:export')")
    @Log(title = "设备管理", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, DeviceDevice deviceDevice) {
        LambdaQueryWrapper<DeviceDevice> wrapper = getWrapper(deviceDevice);
        List<DeviceDevice> list = deviceDeviceService.list(wrapper);
        ExcelUtil<DeviceDevice> util = new ExcelUtil<DeviceDevice>(DeviceDevice.class);
        util.exportExcel(response, packList(list), "设备管理数据");
    }

    /**
     * 获取设备管理详细信息
     */
    @PreAuthorize("@ss.hasPermi('device:device:query')")
    @GetMapping(value = "/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id) {
        DeviceDevice entity = deviceDeviceService.getById(id);
        if (null != entity.getTypeId()) {
            DeviceType type = deviceTypeService.getById(entity.getTypeId());
            if (null != type) {
                entity.setTypeName(type.getName());
            }

            String tableName = DeviceConstants.TABLE_NAME + id + "_";
            List<IotData> dataVos = new ArrayList<>();
            IotData dataVo = null;
            if ("0".equals(entity.getIsDt())) {
                if(StringUtils.isNotEmpty(entity.getMasterCode())){
                    String addressBitmap = redisTemplate.opsForValue().get(DeviceConstants.GGD);
                    //主测点与公共点组成一个测试点
                    if(StringUtils.isNotEmpty(addressBitmap)){
                         String redisName = tableName + entity.getMasterCode()+"_"+ addressBitmap;
                        dataVo = JSONObject.parseObject(redisTemplate.opsForValue().get(redisName), IotData.class);
                        if (null != dataVo) {
                            String[] bitmaps = dataVo.getAddress().split("_");
                            String name = DictUtils.getDictLabel(DeviceConstants.DEVICE_BITMAP, bitmaps[0]) + DictUtils.getDictLabel(DeviceConstants.DEVICE_BITMAP, bitmaps[1]);
                            dataVo.setAddress(name);
                            dataVos.add(dataVo);
                        }
                    }
                    //主测点与每个从测点组成一个测试点
                    if(StringUtils.isNotEmpty(entity.getSlaveCode())){
                        String[] slaveCodes = entity.getSlaveCode().split(",");
                        String bitMap = null;
                        for(int i = 0; i < slaveCodes.length; i++){
                            bitMap = entity.getMasterCode() + "_" +slaveCodes[i];
                            String redisName = tableName + bitMap;
                            dataVo = JSONObject.parseObject(redisTemplate.opsForValue().get(redisName), IotData.class);
                            if (null != dataVo) {
                                String[] bitmaps = dataVo.getAddress().split("_");
                                String name = DictUtils.getDictLabel(DeviceConstants.DEVICE_BITMAP, bitmaps[0]) + DictUtils.getDictLabel(DeviceConstants.DEVICE_BITMAP, bitmaps[1]);
                                dataVo.setAddress(name);
                                dataVos.add(dataVo);
                            }
                        }
                    }
                }
            } else {
                if(StringUtils.isNotEmpty(entity.getAddressBitmap())){
                    tableName += entity.getAddressBitmap().replaceAll(",", "_");
                    dataVo = JSONObject.parseObject(redisTemplate.opsForValue().get(tableName), IotData.class);
                    if (null != dataVo) {
                        String[] bitmaps = dataVo.getAddress().split("_");
                        String name = DictUtils.getDictLabel(DeviceConstants.DEVICE_BITMAP, bitmaps[0]) + DictUtils.getDictLabel(DeviceConstants.DEVICE_BITMAP, bitmaps[1]);
                        dataVo.setAddress(name);
                        dataVos.add(dataVo);
                    }
                }
            }
            entity.setDataVos(dataVos);
            //地址位别名
            if (StringUtils.isNotEmpty(entity.getAddressBitmap())) {
                String[] bitmaps = entity.getAddressBitmap().split(",");
                String name = DictUtils.getDictLabel(DeviceConstants.DEVICE_BITMAP, bitmaps[0]) + DictUtils.getDictLabel(DeviceConstants.DEVICE_BITMAP, bitmaps[1]);
                entity.setDzw(name);
            }
            //主测点别名
            if (StringUtils.isNotEmpty(entity.getMasterCode())) {
                String name = DictUtils.getDictLabel(DeviceConstants.DEVICE_BITMAP, entity.getMasterCode());
                entity.setZdzw(name);
            }
            //从测点别名
            if (StringUtils.isNotEmpty(entity.getSlaveCode())) {
                String[] bitmaps = entity.getSlaveCode().split(",");
                String name = "";
                for (String s : bitmaps) {
                    name += DictUtils.getDictLabel(DeviceConstants.DEVICE_BITMAP, s);
                }
                entity.setCdzw(name);
            }
        }
        return success(entity);
    }

    /**
     * 设备历史数据
     */
    @GetMapping("/hisData/{id}")
    public AjaxResult hisData(@PathVariable("id") Long id) {
        return success(deviceDeviceService.hisData(id));
    }

    /**
     * 手动测试
     */
    @PreAuthorize("@ss.hasPermi('device:device:query')")
    @GetMapping(value = "/test/{id}")
    public AjaxResult testCommand(@PathVariable("id") Long id) {
        DeviceDevice device = deviceDeviceService.getById(id);
        if (null == device) {
            return error("未找到设备信息");
        }
        /**
         * 手动测试下发读取命令
         * 端口1+端口2+类型+频率(2)+电流(2)
         * f1 85 00 09 04 07 12 34 01 13 88 07 d0 60 57
         * 00 09帧数据长度
         * 04 实时采集电阻指令
         * 07 此指令数据长度
         * 12c1p1端口
         * 34c2p2端口
         * 01 测量类型 接地电阻
         * 13 88 测量频率  50.00Hz
         * 07 d0 测量电流  2.000A
         */
        //测量类型
        String type = device.getDxbh();
        //频率
        String frequency = "00 00";
        if (device.getFrequency() != null) {
            frequency = DataUtil.get10To16(device.getFrequency() * 100);
        }
        //电流
        String electricity = "00 00";
        if (device.getElectricity() != null) {
            electricity = DataUtil.get10To16(device.getElectricity() * 1000);
        }
        //实时采集电阻指令类型
        String commandType = "04";
        String commandName = "手动测试" + device.getDeviceName();
        if ("0".equals(device.getIsDt())) {
            //主测点不为空
            if (StringUtils.isNotEmpty(device.getMasterCode())) {
                //主测点与公共点组成一个测试点
                String addressBitmap = redisTemplate.opsForValue().get(DeviceConstants.GGD);
                if (StringUtils.isNotEmpty(addressBitmap)) {
                    String bitMap = device.getMasterCode() + " " + addressBitmap;
                    String command = "f1 85 00 09 " + commandType + " 07 " + bitMap + " " + type + " " + frequency + electricity;
                    iotSerialService.sendCommand(device.getDeptId(), command, commandName);
                    sleep(5000);
                    /*DeviceConstants.SD_FLAG = false;
                    DeviceConstants.SD_CODE = bitMap;
                    while (!DeviceConstants.SD_FLAG){

                        log.info("等待"+bitMap+"测量");
                    }*/
                }
                //主测点与每个从测点组成一个测试点
                if (StringUtils.isNotEmpty(device.getSlaveCode())) {
                    String[] slaveCodes = device.getSlaveCode().split(",");
                    String bitMap = null;
                    for (int i = 0; i < slaveCodes.length; i++) {
                        bitMap = device.getMasterCode() + " " + slaveCodes[i];
                        String command = "f1 85 00 09 " + commandType + " 07 " + bitMap + " " + type + " " + frequency + electricity;
                        iotSerialService.sendCommand(device.getDeptId(), command, commandName);
                        //休眠500ms
                        sleep(5000);
                        /*DeviceConstants.SD_FLAG = false;
                        DeviceConstants.SD_CODE = bitMap;
                        while (!DeviceConstants.SD_FLAG){

                            log.info("等待"+bitMap+"测量");
                        }*/
                    }
                }
            }
        } else {
            //c1p1 c2p2
            String bitMap = device.getAddressBitmap().replace(",", " ");
            String command = "f1 85 00 09 " + commandType + " 07 " + bitMap + " " + type + " " + frequency + electricity;
            iotSerialService.sendCommand(device.getDeptId(), command, commandName);
        }
        return success(true);
    }

    /**
     * 新增设备管理
     */
    @PreAuthorize("@ss.hasPermi('device:device:add')")
    @Log(title = "设备管理", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody DeviceDevice deviceDevice) {
        /**
         * 默认设备状态正常
         */
        deviceDevice.setDeviceStatus("0");
        boolean flag = deviceDeviceService.save(deviceDevice);
        if (flag) {
            //创建数据库
            //deviceDataService.addTable(DeviceConstants.TABLE_NAME + deviceDevice.getId());
            //默认绑定测点
            deviceRuleRefService.autoBindingPointAndRule(deviceDevice.getTypeId(),deviceDevice.getId());
        }
        return toAjax(flag);
    }

    /**
     * 修改设备管理
     */
    @PreAuthorize("@ss.hasPermi('device:device:edit')")
    @Log(title = "设备管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@RequestBody DeviceDevice deviceDevice) {
        return toAjax(deviceDeviceService.updateById(deviceDevice));
    }

    /**
     * 修改导通主从
     */
    @PreAuthorize("@ss.hasPermi('device:device:edit')")
    @Log(title = "修改导通主从", businessType = BusinessType.UPDATE)
    @PutMapping("/editMsCode")
    public AjaxResult editMasterSlave(@RequestBody DeviceDevice deviceDevice) {
        DeviceDevice oldDevice = deviceDeviceService.getById(deviceDevice.getId());
        Boolean flag = false;
        if (null != oldDevice) {
            oldDevice.setMasterCode(deviceDevice.getMasterCode());
            oldDevice.setSlaveCode(deviceDevice.getSlaveCode());
            flag = deviceDeviceService.updateById(oldDevice);
            String bitCode = deviceDevice.getMasterCode() + "," + deviceDevice.getSlaveCode();
            if (flag) {
                if (StringUtils.isNotEmpty(deviceDevice.getMasterCode()) && StringUtils.isNotEmpty(deviceDevice.getSlaveCode())) {
                    //设置主从测试点
                    String command = "f1 83 00 08 " + oldDevice.getDxbh() + "0" + bitCode.split(",").length + bitCode.replaceAll(",", " ");
                    iotSerialService.sendCommand(oldDevice.getDeptId(), command, "设置" + deviceDevice.getDeviceName() + "主从测试点");
                } else {
                    //清除主从测点
                    if (StringUtils.isNotEmpty(oldDevice.getMasterCode())) {
                        iotSerialService.sendCommand(oldDevice.getDeptId(), "f1 85 00 04 0C 02 01 " + oldDevice.getMasterCode(), "清除" + deviceDevice.getDeviceName() + "主从测试点");
                    }
                }
            }
        }
        return toAjax(flag);
    }

    /**
     * 删除设备管理
     */
    @PreAuthorize("@ss.hasPermi('device:device:remove')")
    @Log(title = "设备管理", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public AjaxResult remove(@PathVariable Long[] ids) {
        Boolean flag = deviceDeviceService.removeByIds(new ArrayList<>(Arrays.asList(ids)));
        if (flag) {
            for (Long id : ids) {
                deviceBoardRefService.deleteRefByDeviceId(id);
                //deviceDataService.dropTable(DeviceConstants.TABLE_NAME + id);
                deviceRuleRefService.deleteRefByDeviceId(id);
            }
        }
        return toAjax(flag);
    }

    /**
     * 获取设备选择框列表
     */
    @GetMapping("/optionselect")
    public AjaxResult optionselect() {
        List<DeviceDevice> devices = deviceDeviceService.list();
        return success(devices);
    }


    /**
     * 查询公共方法
     *
     * @param entity
     * @return
     */
    private LambdaQueryWrapper getWrapper(DeviceDevice entity) {
        LambdaQueryWrapper<DeviceDevice> wrapper = new LambdaQueryWrapper<>();
        if (null != entity) {
            if (StringUtils.isNotEmpty(entity.getDeviceName())) {
                wrapper.like(DeviceDevice::getDeviceName, entity.getDeviceName());
            }
            if (StringUtils.isNotEmpty(entity.getDeviceCode())) {
                wrapper.like(DeviceDevice::getDeviceCode, entity.getDeviceCode());
            }
            if (null != entity.getTypeId()) {
                wrapper.eq(DeviceDevice::getTypeId, entity.getTypeId());
            }
        }
        return wrapper;
    }

    /**
     * 结果封装
     *
     * @param list
     * @return
     */
    private List<DeviceDevice> packList(List<DeviceDevice> list) {
        if (CollectionUtils.isNotEmpty(list)) {
            list.forEach(entity -> {
                if (null != entity.getDeptId()) {
                    SysDept dept = deptService.selectDeptById(entity.getDeptId());
                    if (null != dept) {
                        entity.setDeptName(dept.getDeptName());
                    }
                }
                if (null != entity.getTypeId()) {
                    DeviceType type = deviceTypeService.getById(entity.getTypeId());
                    if (null != type) {
                        entity.setTypeName(type.getName());
                    }
                }
            });
        }
        return list;
    }

    /**
     * show
     */
    @PreAuthorize("@ss.hasPermi('device:device:list')")
    @GetMapping("/showList")
    public AjaxResult showList() {
        return success(deviceDeviceService.updateDeviceDataWs());
    }

    /**
     * 重置硬件配置
     * @return
     */
    @PreAuthorize("@ss.hasPermi('device:device:edit')")
    @GetMapping("/resetHardWareConfig")
    public AjaxResult resetHardWareConfig() {
        return success(deviceDeviceService.resetHardWareConfig());
    }

    /**
     * 获取设备数据趋势
     * @param deviceIds 设备ID
     * @param pointIds 测点ID
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @param dataType 数据类型（0电阻 1电压）
     */
    @GetMapping("/dataTrendData")
    public AjaxResult multiTrendData(@RequestParam(value = "deviceIds", required = false) List<Long> deviceIds,
                                     @RequestParam(value = "pointIds", required = false) List<Long> pointIds,
                                     @RequestParam(value = "startTime", required = false) Date startTime,
                                     @RequestParam(value = "endTime", required = false) Date endTime,
                                     @RequestParam(value = "dataType", defaultValue = "0") String dataType) {
        try {
            // 如果未选择设备，则查询所有设备
            if (deviceIds == null || deviceIds.isEmpty()) {
                List<DeviceDevice> allDevices = deviceDeviceService.list();
                deviceIds = allDevices.stream()
                        .map(DeviceDevice::getId)
                        .collect(Collectors.toList());
            }

            // 查询多设备数据
            List<IotData> allData = deviceDataService.selectTrendData(deviceIds, pointIds, startTime, endTime, dataType);

            // 按设备分组构建图表数据
            Map<String, Object> result = new HashMap<>();
            result.put("dataList", allData);

            // 构建多设备图表数据
            Map<String, Object> chartData = buildMultiDeviceChartData(allData, dataType, deviceIds);
            result.put("chartData", chartData);

            return AjaxResult.success(result);
        } catch (Exception e) {
            logger.error("获取多设备数据趋势失败", e);
            return AjaxResult.error("获取数据失败");
        }
    }

    /**
     * 构建多设备图表数据格式
     */
    private Map<String, Object> buildMultiDeviceChartData(List<IotData> dataList, String dataType, List<Long> deviceIds) {
        Map<String, Object> chartData = new HashMap<>();

        // 按设备ID分组数据
        Map<Long, List<IotData>> dataByDevice = dataList.stream()
                .collect(Collectors.groupingBy(IotData::getDeviceId));

        // 获取所有时间点（用于统一X轴）
        Set<String> allTimes = dataList.stream()
                .map(IotData::getTsString)
                .collect(Collectors.toCollection(TreeSet::new));

        String[] timeArray = allTimes.toArray(new String[0]);
        chartData.put("times", timeArray);

        // 为每个设备构建数据系列
        List<Map<String, Object>> seriesList = new ArrayList<>();
        String[] legendData = new String[deviceIds.size()];

        // 定义颜色数组，确保不同设备有不同颜色
        String[] colors = {"#5470c6", "#91cc75", "#fac858", "#ee6666", "#73c0de", "#3ba272", "#fc8452", "#9a60b4"};

        for (int i = 0; i < deviceIds.size(); i++) {
            Long deviceId = deviceIds.get(i);
            List<IotData> deviceData = dataByDevice.get(deviceId);

            if (deviceData != null) {
                // 获取设备信息
                DeviceDevice device = deviceDeviceService.getById(deviceId);
                String deviceName = device != null ? device.getDeviceName() : ("设备" + deviceId);
                legendData[i] = deviceName;

                // 构建该设备的数据点
                List<Object[]> valueArray = new ArrayList<>();
                Map<String, IotData> timeDataMap = deviceData.stream()
                        .collect(Collectors.toMap(IotData::getTsString, data -> data));

                for (String time : timeArray) {
                    IotData data = timeDataMap.get(time);
                    if (data != null) {
                        String value = "0".equals(dataType) ? data.getDz() : data.getDy();
                        valueArray.add(new Object[]{time, value});
                    } else {
                        // 没有数据的时间点设为null
                        valueArray.add(new Object[]{time, null});
                    }
                }

                Map<String, Object> series = new HashMap<>();
                series.put("name", deviceName);
                series.put("type", "line");
                series.put("data", valueArray);
                series.put("smooth", true);
                series.put("symbol", "circle");
                series.put("symbolSize", 6);
                series.put("lineStyle", Collections.singletonMap("width", 2));
                series.put("itemStyle", Collections.singletonMap("color", colors[i % colors.length]));

                seriesList.add(series);
            }
        }

        chartData.put("series", seriesList);
        chartData.put("legend", legendData);

        return chartData;
    }

    @GetMapping("/getDeviceStatus")
    public AjaxResult getDeviceStatus() {
        JSONObject jsonObject = new JSONObject();
        List<DeviceDevice> devices = deviceDeviceService.list();
        if (CollectionUtils.isEmpty(devices)) {
            jsonObject.put("deviceTotal", "0");
            jsonObject.put("deviceZc", "0");
            jsonObject.put("deviceGj", "0");
        }else{
            int deviceTotal = devices.size();
            int deviceZc = devices.stream().filter(deviceDevice -> "0".equals(deviceDevice.getDeviceStatus())).collect(Collectors.toList()).size();
            int deviceGj = devices.stream().filter(deviceDevice -> "1".equals(deviceDevice.getDeviceStatus())).collect(Collectors.toList()).size();
            jsonObject.put("deviceTotal", deviceTotal);
            jsonObject.put("deviceZc", deviceZc);
            jsonObject.put("deviceGj", deviceGj);
        }
        return success(jsonObject);
    }
}
